﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Linq.Expressions;

using LINQToolKit;
using LINQToolKit.Collections;
using LINQToolKit.Configuration;
using LINQToolKit.Query;
using LINQToolKit.Web;
using LINQToolKit.Douban.Processors;
using LINQToolKit.Mapping;
using LINQToolKit.Processors;

namespace LINQToolKit.Douban
{
    public sealed class DoubanQueryProvider : QueryProvider
    {
        public DoubanQueryProvider(IMappingSource mappingSource, IRequest request)
            :this(mappingSource, null, request)
        {
        }

        public DoubanQueryProvider(IMappingSource mappingSource, ProviderConfiguration config, IRequest request)
            : base(mappingSource, config, request)
        {

        }

        public override IEnumerable Select(Type entityType, Uri source)
        {
            if (!source.Query.Contains("max-results"))
            {
                string url = source.OriginalString;

                if (source.Query.Length > 0)
                {
                    url += "&max-results=50";
                }
                else
                {
                    url += "?max-results=50";
                }

                source = new Uri(url);
            }

            return base.Select(entityType, source);
        }

        public override object SelectOne(Type entityType, Uri source)
        {
            if (!source.Query.Contains("max-results"))
            {
                string url = source.OriginalString;

                if (source.Query.Length > 0)
                {
                    url += "&max-results=1";
                }
                else
                {
                    url += "?max-results=1";
                }

                source = new Uri(url);
            }

            return base.SelectOne(entityType, source);
        }

        protected override IEnumerable CreateQuerySession(
            RequestResult rawResult, Type entityType, IEnumerable rawData)
        {
            if (rawResult.RequestUrl.OriginalString.Contains("&session=true"))
            {
                return rawData;
            }

            var doc = XElement.Parse(rawResult.Message);
            XNamespace ns = "http://a9.com/-/spec/opensearchrss/1.0/";
            var startIndex = 0;
            var itemsPerPage = 0;
            var totalResults = 0;

            if (doc.Element(ns + "startIndex") != null)
            {
                startIndex = Convert.ToInt32(doc.Element(ns + "startIndex").Value);
            }

            if (doc.Element(ns + "itemsPerPage") != null)
            {
                itemsPerPage = Convert.ToInt32(doc.Element(ns + "itemsPerPage").Value);
            }

            if (doc.Element(ns + "totalResults") != null)
            {
                totalResults = Convert.ToInt32(doc.Element(ns + "totalResults").Value);
            }

            if (totalResults <= (itemsPerPage + startIndex))
            {
                // appending data or has loaded all data
                return base.CreateQuerySession(rawResult, entityType, rawData);
            }
            else
            {
                var args = new object[] {
                    this, rawData, rawResult.RequestUrl.OriginalString, totalResults, itemsPerPage
                };

                return (IEnumerable)Activator.CreateInstance(
                    typeof(DoubanQuerySession<>).MakeGenericType(entityType), args);
            }
        }
    }
}
